#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <cstdlib>
#include <sys/epoll.h>
#include <unistd.h>

// 一般处理IO的时候，我们只有三种接口需要处理
// 处理读取
// 处理写入
// 处理异常
#define SIZE 128
#define NUM 64

class Event;
class Reactor;

//callback_t，函数指针类型
typedef int (*callback_t)(Event *ev); 

//先Event描述，再Reactor组织、管理 
class Event //事件结构体
{
public:
    //对应的文件描述符
    int sock;

    //sock对应的输入缓冲区
    std::string inbuffer;
    //sock对应的输出缓冲区
    std::string outbuffer;

    //sock设置回调
    callback_t recver;//作用：将数据拷贝到inbuffer
    callback_t sender;
    callback_t errorer;

    //设置Event回指Reactor的指针
    Reactor *R;

public:
    Event()
    {
        sock = -1;

        recver = nullptr;
        sender = nullptr;
        errorer = nullptr;
        
        R = nullptr;
    }
    
    //设置回调方法
    void RegisterCallback(callback_t _recver, callback_t _sender, 
    callback_t _errorer)
    {
        recver = _recver;
        sender = _sender;
        errorer = _errorer;
    }

    ~Event(){}
};

//因为每个Event(fd)都有自己的回调函数，所以不需要关心任
//何sock的类型(listen/正常读写)，
//只关心如何进行使用该类对Event进行管理。Reactor : Event = 1 : n;
class Reactor
{
private:
    //保存epol_fd，有epoll_fd才能找到epoll模型，才能知晓有没有事件就绪
    int epfd; 

    //我的Reactor类管理的所有的Event的集合关联fd和Event*，一但底层有数据
    //就绪了，就可以拿fd找到对应的Event，然后调用回调，将数据读到inbuffer
    std::unordered_map<int, Event *> events; 

    //先用Event描述，再用unordered_map组织，Reactor再管理
public:
    Reactor() : epfd(-1){}

    //初始化
    void InitReactor()
    {
        epfd = epoll_create(SIZE);//创建epoll模型
        if (epfd < 0){
            std::cerr << "epoll_create error" << std::endl;
            exit(2);
        }
        std::cout << "InitReactor success" << std::endl;
    }

    //向unordered_map中插入 fd-Event 的节点
    bool InsertEvent(Event *evp, uint32_t evs)
    {
        //1. 将evp中的sock插入到epoll中
        //用evs(关心的事件)和evp中的sock构建epoll_event
        struct epoll_event ev; 
        ev.events = evs;
        ev.data.fd = evp->sock;
        if (epoll_ctl(epfd, EPOLL_CTL_ADD, evp->sock, &ev) < 0)
        {
            std::cerr << "epoll_ctl add event failed" << std::endl;
            return false;
        }
        //2. 将evp本身插入到unordered_map中
        events.insert({evp->sock, evp});
    }
    
    //删除unordered_map中的节点
    void DeleteEvent(Event *evp)
    {
        int sock = evp->sock;
        auto iter = events.find(sock);
        if (iter != events.end())
        {
            // 1. 将sock从epoll中删除
            epoll_ctl(epfd, EPOLL_CTL_DEL, sock, nullptr);

            // 2. 将特定的evp从unordered_map中移除
            events.erase(iter);

            // 3. close
            close(sock);

            // 4. 删除event节点
            delete evp;
        }
    }

    //关于修改，使能读写
    bool EnableRW(int sock, bool enbread, bool enbwrite)
    {
        //构建要关心的事件
        struct epoll_event ev;
        ev.events = EPOLLET | (enbread ? EPOLLIN : 0) 
        | (enbwrite ? EPOLLOUT : 0);
        ev.data.fd = sock;

        //修改epfd关心的事件
        if (epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ev) < 0)
        {
            std::cerr << "epoll_ctl mod event failed" << std::endl;
            return false;
        }
    }

    //判断sock是否合法
    bool IsSockOk(int sock)
    {
        auto iter = events.find(sock);
        return iter != events.end();
    }

    //就绪事件的派发器逻辑
    //原理：epoll来告诉你那些文件描述符已就绪，用fd找到Event，调用里面的
    //      读写回调，读到自己的输入输出缓冲区。
    //反应堆模式(Reactor模式)：处理并发I/O比较常见的一种设计模式，用于同步I/O。
    //其中心思想是：将所有要处理的I/O事件注册到一个中心I/O多路复用器上，同时
    //             主线程阻塞在多路复用器上；一旦有I/O事件到来或者是准备就绪，
    //             多路复用器返回并将相应的I/O事件分发到对应的处理器中。
    void Dispatcher(int timeout)
    {
        struct epoll_event revs[NUM];
        int n = epoll_wait(epfd, revs, NUM, timeout);
        for (int i = 0; i < n; i++)
        {
            int sock = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            //代表差错处理, 将所有的错误问题全部转化成为让IO函数去解决
            if (revents & EPOLLERR)
                revents |= (EPOLLIN | EPOLLOUT);
            if (revents & EPOLLHUP)
                revents |= (EPOLLIN | EPOLLOUT);

            //读数据就绪
            if (revents & EPOLLIN)
            {
                //直接调用回调方法，执行对应的读取
                if (IsSockOk(sock) && events[sock]->recver)
                    events[sock]->recver(events[sock]); 
                    //events[sock]就是sock所对应的Event *
            }       //recver是对应的回调函数

            //写数据就绪
            if (revents & EPOLLOUT)
            {
                if (IsSockOk(sock) && events[sock]->sender)
                    events[sock]->sender(events[sock]);
            }
        }
    }

    ~Reactor() {}
};

